


#ifndef __SHELL_H__
#define __SHELL_H__



#include "qelib.h"



#define SHELL_SHOW_INFO         1
#define SHELL_HELP_LIST_VAR     1

#define SHELL_PARAM_MAX_NUM     8
#define SHELL_HISTORY_MAX_NUM   4

#define SHELL_VERSION           "1.0.0"
#define SHELL_DEFAULT_USER      "debug"
#define SHELL_DEFAULT_PSWD      "12345"


/*
 * Command Attribute
 * =================
 */
#define SHELL_CMD_ATTR_ENABLE_UNCHECKED     12
#define SHELL_CMD_ATTR_ENABLE_RETURN        13
#define SHELL_CMD_ATTR_READ_ONLY            14

#define SHELL_CMD_PERM(perm)    (perm & 0x000000FF)
#define SHELL_CMD_TYPE(type)    ((type & 0x0000000F) << 8)
#define SHELL_CMD_EUCK          (1 << SHELL_CMD_ATTR_ENABLE_UNCHECKED)
#define SHELL_CMD_ERET          (1 << SHELL_CMD_ATTR_ENABLE_RETURN)
#define SHELL_CMD_RDOY          (1 << SHELL_CMD_ATTR_READ_ONLY)
#define SHELL_CMD_PNUM(num)     ((num & 0x0000000F) << 16)


/*
 * Command Declaration
 * ===================
 */
#if (SHELL_USING_CMD_EXPORT == 0)

    /**
     * @brief shell command item
     * 
     * @param _attr command attribute
     * @param _name command name
     * @param _func command function
     * @param _desc command description
     */
    #define SHELL_CMD_ITEM(_attr, _name, _func, _desc) \
        {\
            .attr.value = _attr, \
            .data.cmd.name = #_name,\
            .data.cmd.function = (int (*)())_func,\
            .data.cmd.desc = #_desc\
        }

    /**
     * @brief shell function item
     * 
     * @param _pnum function parameters number
     * @param _name function name
     * @param _func function function
     * @param _desc function description
     */
    #define SHELL_CMD_FUNC(_pnum, _name, _func, _desc) \
        SHELL_CMD_ITEM(SHELL_CMD_PNUM(_pnum)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), \
            _name, _func, _desc)

    /**
     * @brief shell execute item
     * 
     * @param _name execute name
     * @param _func execute function
     * @param _desc execute description
     */
    #define SHELL_CMD_EXEC(_name, _func, _desc) \
        SHELL_CMD_ITEM(SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), \
            _name, _func, _desc)

    /**
     * @brief shell variable item
     * 
     * @param _attr  variable attribute
     * @param _name  variable name
     * @param _value variable value
     * @param _desc  variable description
     */
    #define SHELL_VAR_ITEM(_attr, _name, _value, _desc) \
        { \
            .attr.value = _attr, \
            .data.var.name = #_name, \
            .data.var.value = (void *)_value, \
            .data.var.desc = #_desc \
        }

    /**
     * @brief shell int variable item
     * 
     * @param _name  variable name
     * @param _value variable value
     * @param _desc  variable description
     */
    #define SHELL_VAR_INT(_name, _value, _desc) \
        SHELL_VAR_ITEM(SHELL_CMD_TYPE(SHELL_TYPE_VAR_INT), _name, _value, _desc)

    /**
     * @brief shell string variable item
     * 
     * @param _name  variable name
     * @param _value variable value
     * @param _desc  variable description
     */
    #define SHELL_VAR_STR(_name, _value, _desc) \
        SHELL_VAR_ITEM(SHELL_CMD_TYPE(SHELL_TYPE_VAR_STRING), _name, _value, _desc)

    /**
     * @brief shell user item
     * 
     * @param _attr user attribute
     * @param _name user name
     * @param _pswd user password
     * @param _desc user description
     */
    #define SHELL_USR_ITEM(_name, _pswd, _desc) \
        {\
            .attr.value = SHELL_CMD_TYPE(SHELL_TYPE_USR), \
            .data.usr.name = _name, \
            .data.usr.password = _pswd, \
            .data.usr.desc = #_desc \
        }

    /**
     * @brief shell key item
     * 
     * @param _value user name
     * @param _func user password
     * @param _desc user description
     */
    #define SHELL_KEY_ITEM(_value, _func, _desc) \
        {\
            .attr.value = SHELL_CMD_TYPE(SHELL_TYPE_KEY), \
            .data.key.value = _value, \
            .data.key.function = (void (*)(shell_t *))_func, \
            .data.key.desc = #_desc \
        }
#else

#endif


/*
 * Command Type
 * ============
 */
typedef enum {
    SHELL_TYPE_CMD_MAIN = 0,
    SHELL_TYPE_CMD_FUNC,
    SHELL_TYPE_VAR_INT,
    SHELL_TYPE_VAR_SHORT,
    SHELL_TYPE_VAR_CHAR,
    SHELL_TYPE_VAR_STRING,
    SHELL_TYPE_VAR_POINT,
    SHELL_TYPE_VAR_NODE,
    SHELL_TYPE_USR,
    SHELL_TYPE_KEY,
} shell_cmdtype_e;


/*
 * Number Type
 * ===========
 */
typedef enum
{
    SHELL_NUM_DEC,
    SHELL_NUM_BIN,
    SHELL_NUM_OCT,
    SHELL_NUM_HEX,
    SHELL_NUM_FLOAT
} shell_numtype_e;


enum
{
#if SHELL_SHOW_INFO == 1
    SHELL_TEXT_INFO,
#endif
    SHELL_TEXT_CMD_TOO_LONG,
    SHELL_TEXT_CMD_LIST,
    SHELL_TEXT_VAR_LIST,
    SHELL_TEXT_USER_LIST,
    SHELL_TEXT_KEY_LIST,
    SHELL_TEXT_CMD_NOT_FOUND,
    SHELL_TEXT_POINT_CANNOT_MODIFY,
    SHELL_TEXT_VAR_READ_ONLY_CANNOT_MODIFY,
    SHELL_TEXT_NOT_VAR,
    SHELL_TEXT_VAR_NOT_FOUND,
    SHELL_TEXT_HELP_HEADER,
    SHELL_TEXT_PASSWORD_HINT,
    SHELL_TEXT_PASSWORD_ERROR,
    SHELL_TEXT_CLEAR_CONSOLE,
    SHELL_TEXT_CLEAR_LINE,
    SHELL_TEXT_TYPE_CMD,
    SHELL_TEXT_TYPE_VAR,
    SHELL_TEXT_TYPE_USER,
    SHELL_TEXT_TYPE_KEY,
    SHELL_TEXT_TYPE_NONE,
#if SHELL_EXEC_UNDEF_FUNC == 1
    SHELL_TEXT_PARAM_ERROR,
#endif
};

typedef struct {
    void *var;          /* variable reference */
    int (*get)();       /* variable get method */
    int (*set)();       /* variable set method */
} shell_node_attr;


/* 
 * Shell and Command Struct
 * ==============
 */
typedef struct {

    struct {
        const struct shell_command *user;
        int active_time;
        char *path;
    } info;

    struct {
        int key;
        int cursor;
        int length;
        int nr_params;
        int bufsz;
        qe_gbuf *buf;
        char *param[SHELL_PARAM_MAX_NUM];
    } parser;

#if SHELL_HISTORY_MAX_NUM > 0
    struct {
        char *item[SHELL_HISTORY_MAX_NUM];  /**< history item */
        int number;                         /**< history record number */
        int record;                         /**< current record location */
        int offset;                         /**< current record offset */
    } history;
#endif

    struct {
        qe_u8 initialized:1;
        qe_u8 checked:1;
        qe_u8 active:1;
        qe_u8 tab:1;
    } status;

    struct {
        void *base;
        int count;
    } cmdtab;

    int (*read) (char *, int);
    int (*write)(char *, int);

} shell_t;

typedef struct shell_command {
    
    union {
        struct {
            qe_u8 permission:8;         /* command permission */
            qe_u8 type:4;               /* command type */
            qe_u8 enable_unckecked:1;   /* command exec with password unchecked */
            qe_u8 enable_return:1;      /* command exec print return value */
            qe_u8 read_only:1;          /* command read only */
            qe_u8 reserve:1;            
            qe_u8 num_params:4;         /* command parameters number */
        } attrs;
        qe_u32 value;
    } attr;

    union {
        struct {
            const char *name;       /* command name */
            int (*function)();      /* command call function */
            const char *desc;       /* command description */
        } cmd;
        struct {
            const char *name;       /* variable name */
            void *value;            /* variable value pointer */
            const char *desc;       /* variable description */
        } var;
        struct
        {
            const char *name;                                   /**< 用户名 */
            const char *password;                               /**< 用户密码 */
            const char *desc;                                   /**< 用户描述 */
        } usr;
        struct {
            int value;                      /* key value */
            void (*function)(shell_t *);    /* key call function */
            const char *desc;               /* key call description */
        } key;
    } data;
} shell_command_t;



/*
 * Public Function
 * ===============
 */
#define SHELL_LOGNAME      "shell"
#define shell_debug(...)   qelog_debug(SHELL_LOGNAME,   __VA_ARGS__)
#define shell_info(...)    qelog_info(SHELL_LOGNAME,    __VA_ARGS__)
#define shell_notice(...)  qelog_notice(SHELL_LOGNAME,  __VA_ARGS__)
#define shell_warning(...) qelog_warning(SHELL_LOGNAME,    __VA_ARGS__)
#define shell_error(...)   qelog_error(SHELL_LOGNAME,   __VA_ARGS__)
#define shell_fatal(...)   qelog_fatal(SHELL_LOGNAME,     __VA_ARGS__)

qe_ret shell_init(shell_t *sh, int bufsz, void *tab, int tabsize, 
    const char *user, int (*read)(char *, int), int (*write)(char *, int));
shell_t *shell_get_ctx(void);
void shell_task(shell_t *sh);
void key_up(shell_t *sh);
void key_down(shell_t *sh);
void key_left(shell_t *sh);
void key_right(shell_t *sh);
void key_enter(shell_t *sh);
void cmd_help(int argc, char *argv[]);
int cmd_setvar(char *name, int value);
int cmd_getvar(char *name);
#endif /* __SHELL_H__ */